<?php

namespace Twoir\Anroute\Routing;

use DirectoryIterator;
use ReflectionClass;
use ReflectionMethod;
use Twoir\Anroute\Attributes\Route;
use Twoir\Anroute\Attributes\Prefix;
use Twoir\Anroute\Attributes\Name;

class Router
{
    private array $routes = [];
    private array $namedRoutes = [];

    public function registerControllers(array $controllerNamespaces): void
    {
        foreach ($controllerNamespaces as $namespace) {
            $controllers = $this->findControllers($namespace);
            foreach ($controllers as $controller) {
                $this->registerController($controller);
            }
        }
    }

    private function findControllers(string $namespace): array
    {
        $controllers = [];
        $dir = new DirectoryIterator(dirname(__DIR__, 2) . "/app/Controllers");

        foreach ($dir as $file) {
            if ($file->isFile() && 'php' === $file->getExtension()) {
                $className = $namespace . '\\' . $file->getBasename('.php');
                if (class_exists($className)) {
                    $controllers[] = $className;
                }
            }
        }

        return $controllers;
    }

    private function registerController(string $className): void
    {
        $reflection = new ReflectionClass($className);

        // 获取类上的 Prefix 注解
        $prefix = '';
        $classAttributes = $reflection->getAttributes(Prefix::class);
        if (!empty($classAttributes)) {
            /** 
             * @var Prefix $prefixAttribute 
             */
            $prefixAttribute = $classAttributes[0]->newInstance();
            $prefix = $prefixAttribute->value;
        }

        // 遍历所有方法注册路由
        foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            $routeAttributes = $method->getAttributes(Route::class);

            foreach ($routeAttributes as $routeAttribute) {
                /** 
                 * @var Route $route 
                 */
                $route = $routeAttribute->newInstance();

                // 合并类前缀和方法路径
                $fullPath = rtrim($prefix, '/') . '/' . ltrim($route->path, '/');

                // 获取方法上的 Name 注解
                $name = null;
                $nameAttributes = $method->getAttributes(Name::class);
                if (!empty($nameAttributes)) {
                    /** 
                     * @var Name $nameAttribute 
                     */
                    $nameAttribute = $nameAttributes[0]->newInstance();
                    $name = $nameAttribute->value;

                    $this->namedRoutes[$name] = [
                        'path' => $fullPath,
                        'method' => strtoupper($route->method),
                        'handler' => [$className, $method->getName()]
                    ];
                }

                // 注册普通路由
                $this->routes[] = [
                    'path' => $fullPath,
                    'method' => strtoupper($route->method),
                    'handler' => [$className, $method->getName()]
                ];
            }
        }
    }

    public function getRoutes(): array
    {
        return $this->routes;
    }

    public function getNamedRoutes(): array
    {
        return $this->namedRoutes;
    }

    public function getRouteByName(string $name): ?array
    {
        return $this->namedRoutes[$name] ?? null;
    }

    public function generateUrl(string $name, array $pathParams = [], array $queryParams = []): ?string
    {
        $route = $this->getRouteByName($name);
        if (!$route) {
            return null;
        }

        $path = $route['path'];

        // 替换路径参数
        foreach ($pathParams as $key => $value) {
            $path = preg_replace("/\{{$key}\}/", rawurlencode($value), $path);
        }

        // 检查是否还有未替换的参数
        if (preg_match("/\{.*?\}/", $path)) {
            return null; // 参数不全
        }

        // 拼接查询参数
        if (!empty($queryParams)) {
            $queryString = http_build_query($queryParams);
            $path .= '?' . $queryString;
        }

        return $path;
    }
}
